# gozerbot/plugins.py
#
#

""" provide plugin infrastructure """

__copyright__ = 'this file is in the public domain'

from gozerbot.datadir import datadir
from gozerbot.users import users
from gozerbot.monitor import outmonitor, saymonitor
from gozerbot.generic import rlog, handle_exception, checkchan
from gozerbot.myimport import my_import
from gozerbot.persist import Persist
from gozerbot.config import config
from gozerbot.commands import cmnds
from gozerbot.callbacks import callbacks
from gozerbot.redispatcher import rebefore, reafter
from gozerbot.gozertest import gtest
from gozerbot.ircevent import makeargrest
from gozerbot.aliases import aliascheck
import os.path, thread

class Plugins(object):

    """ hold all the plugins """

    def __init__(self):
        self.plugs = {}
        self.reloadlock = thread.allocate_lock()
        self.plugdeny = Persist(datadir + '/plugdeny')
        if not self.plugdeny.data:
            self.plugdeny.data = []

    def __getitem__(self, item):
        """ return plugin """
        if self.plugs.has_key(item):
            return self.plugs[item]

    def exist(self, name):
        """ see if plugin <name> exists """
        if self.plugs.has_key(name):
            return 1 

    def disable(self, name):
        """ prevent plugins <name> to be loaded """
        if name not in self.plugdeny.data:
            self.plugdeny.data.append(name)
            self.plugdeny.save()

    def enable(self, name):
        """ enable plugin <name> """
        if name in self.plugdeny.data:
            self.plugdeny.data.remove(name)
            self.plugdeny.save()

    def list(self):
        """ list of registered plugins """
        return self.plugs.keys()

    def regplugin(self, mod, name):
        """ register plugin """
        name = name.lower()
        mod = mod.lower()
        modname = mod + '.' + name
        # see if plugin is in deny
        if name in self.plugdeny.data:
            rlog(10, 'plugins', '%s in deny .. not loading' % name)
            return 0
        # if plugin is already registered unload it
        if self.plugs.has_key(name):
            rlog(0, 'plugins', 'overloading %s plugin with %s version' \
% (name, mod))
            self.unloadnosave(name)
        # import the plugin
        try:
            plug = my_import(modname)
            self.plugs[name] = plug
        except Exception, ex:
            self.unload(name)
            raise
        # call init function in plugin
        try:
            rlog(0, 'plugins', 'calling %s init()' % name)
            result = plug.init()
            if not result:
                return 
        except AttributeError:
            pass
        except:
            self.unload(name)
            raise
        return 1

    def regplugins(self):
        """ register plugins """
        # first do plugins in the "plugs" directory
        # if database is enabled check "dbplugs" directory
        dbplugs = []
        if config['dbenable'] and os.path.isfile('dbplugs/__init__.py'):
            plugs = my_import('dbplugs')
            for plug in plugs.__all__:
                try:
                    self.regplugin('dbplugs', plug)
                    dbplugs.append(plug)
                except:
                    handle_exception()
        plugs = my_import('plugs')
        for plug in plugs.__all__:
            if plug not in dbplugs:
                try:
                    self.regplugin('plugs', plug)
                except:
                    handle_exception()
        # check for "myplugs" directory
        if os.path.isfile('myplugs/__init__.py'):
            plugs = my_import('myplugs')
            for plug in plugs.__all__:
                try:
                    self.regplugin('myplugs', plug)
                except:
                    handle_exception()
        # log loaded plugs
        pluglist = self.list()
        pluglist.sort()
        rlog(10, 'plugins', 'loaded %s' % ' .. '.join(pluglist))
        # see if there is a permoverload file and if so use it to overload
        # permissions based on function name
        try:
            overloadfile = open('permoverload','r')
        except IOError:
            return
        try:
            for i in overloadfile:
                (funcname, perm) = i.split(',', 1)
                perm = perm.strip()
                if self.permoverload(funcname, perm):
                    rlog(10, 'plugins', '%s permission set to %s' % \
(funcname, perm))
                else:
                    rlog(10, 'plugins', 'overload of %s failed' % funcname)
        except:
            handle_exception()

    def available(self):
        """ available pluginsregister """
        result = {}
        # first do plugins in the "plugs" directory
        plugs = my_import('plugs')
        for plug in plugs.__all__:
            result[plug] = 1
        # if database is enabled check "dbplugs" directory
        if config['dbenable'] and os.path.isfile('dbplugs/__init__.py'):
            plugs = my_import('dbplugs')
            for plug in plugs.__all__:
                result[plug] = 1
        # check for "myplugs" directory
        if os.path.isfile('myplugs/__init__.py'):
            plugs = my_import('myplugs')
            for plug in plugs.__all__:
                result[plug] = 1
        resultlist = result.keys()
        resultlist.sort()
        return resultlist

    def saveplug(self, plugname):
        """ call save on items in plugins savelist """
        try:
            savelist = self.plugs[plugname].savelist
            for saveitem in savelist:
                try:
                    saveitem.save()
                except:
                    handle_exception()
        except AttributeError:
            pass
        except KeyError:
            pass

    def save(self):
        """ call registered plugins save """
        for plug in self.plugs.values():
            try:
                savelist = getattr(plug, 'savelist')
            except AttributeError:
                continue
            for item in savelist:
                try:
                    item.save()
                except:
                    handle_exception()
    def exit(self):
        """ call registered plugins save """
        self.save()
        for name, plug in self.plugs.iteritems():
            try:
                shutdown = getattr(plug, 'shutdown')
                rlog(1, 'plugins', 'shutting down %s' % name)
                shutdown()
            except AttributeError:
                continue

    def reload(self, mod, name):
        """ reload plugin """
        ok = 0
        # save data in plugin's savelist before we try to reload
        modname = mod + '.' + name
        if mod == 'dbplugs' and not config['dbenable']:
            rlog(10, 'plugins', 'db not enabled .. not reloading %s' % \
modname) 
            return 
        # unload plugin
        self.unload(name)
        # see if module is already loaded if not load it
        if not self.plugs.has_key(name):
            self.plugs[name] = my_import(modname)
        # reload the thing
        self.plugs[name] = reload(self.plugs[name])
        # call init now the module has been reloaded
        try:
            rlog(0, 'plugins', 'calling %s init()' % modname)
            ok = self.plugs[name].init()
        except (AttributeError, KeyError):
            ok = 1
            pass
        # enable plugin
        self.enable(name)
        if not ok:
            rlog(10, 'plugins', '%s module init failed' % name)
            return 0
        rlog(10, 'plugins', 'reloaded plugin %s' % modname)
        return 1

    def unload(self, plugname):
        """ unload plugin """
        # call plugins shutdown function if available
        try:
            self.plugs[plugname].shutdown()
            rlog(0, 'plugins', '%s shutdown called' % plugname)
        except (AttributeError, KeyError):
            pass
        self.saveplug(plugname)
        return self.unloadnosave(plugname)

    def unloadnosave(self, plugname):
        """ unload plugin without saving """
        try:
            cmnds.unload(plugname)
            callbacks.unload(plugname)
            rebefore.unload(plugname)
            reafter.unload(plugname)
            saymonitor.unload(plugname)
            outmonitor.unload(plugname)
            gtest.rmtests(plugname)
            if self.plugs.has_key(plugname):
                del self.plugs[plugname]
        except Exception, ex:
            handle_exception()
            return 0
        return 1

    def whereis(self, what):
        """ locate what in plugins """
        return cmnds.whereis(what)

    def permoverload(self, funcname, perm):
        """ overload permission of a function """
        if not rebefore.permoverload(funcname, perm):
            if not cmnds.permoverload(funcname, perm):
                if not reafter.permoverload(funcname, perm):
                    return 0
        return 1

    def woulddispatch(self, ievent):
        """ see if ievent would dispatch """
        result = []
        com = rebefore.getcallback(ievent.txt)
        if com:
            result =  (rebefore, com)
        else:
            com = cmnds.getcommand(ievent.txt)
            if com:
                result = (cmnds, com)
            else:
                com = reafter.getcallback(ievent.txt)
                if com:
                    result = (reafter, com)
        if result:
            if users.allowed(ievent.userhost, result[1].perms):
                return result
        return (None, None)

    def trydispatch(self, bot, ievent):
        """ try to dispatch ievent """
        aliascheck(ievent)
        (what, com) = self.woulddispatch(ievent)
        if what:
            return self.dispatch(what, com, bot, ievent)

    def dispatch(self, what, com, bot, ievent):
        """ dispatch ievent """
        if bot.stopped:
            return 0
        # check for user provided channel
        chan = checkchan(ievent.txt)
        if chan != None:
            (channel, ievent.txt) = chan
            if ievent.msg:
                ievent.printto = ievent.nick
            else:
                ievent.printto = ievent.channel
            ievent.channel = channel
        makeargrest(ievent)
        bot.commandhandler.put(ievent.speed, what, com, bot, ievent)
        return 1
	
plugins = Plugins()
